import logging
import os

import pandas as pd
import numpy as np
from sklearn.decomposition import PCA
from tqdm import tqdm

from util.file_util import get_static_file_path, get_last_file_name
from util.log_util import get_logger

# 将 patchs * 2048维度的数据压缩为 k * pca_dim数据，表示svs的特征向量。k、pca_dim默认值为128，k表示提取多少个向量，pca_dim表示向量的维度
# 注意：pca_dim < min(K, 特征向量维度)
def extract_topk_patches(csv_path: str, k: int = 128, pca_dim: int = 128) -> np.ndarray:
    # 1. 读取 CSV 特征
    df = pd.read_csv(csv_path)

    # 2. 提取特征矩阵（假设第1列是 patch_id）
    features = df.iloc[:, 1:].values  # shape: [N_patches, 2048]

    # 3. Top-K 策略（使用 L2 范数筛选重要 patch）
    norms = np.linalg.norm(features, axis=1)  # 每行向量的模长
    topk_indices = np.argsort(norms)[-k:]  # 选出 L2 值最大的 K 个
    topk_features = features[topk_indices]  # shape: [K, 2048]

    # 4. 特征降维（PCA）
    pca = PCA(n_components=pca_dim)
    reduced_features = pca.fit_transform(topk_features)  # shape: [K, 128]

    return reduced_features  # 最终为 100 × 128 的 numpy array

# 目的：对获取的特征进行选举并降维
if __name__ == '__main__':
    get_logger(logging.INFO)
    # 将所有的svs图像通过Top-K选举和降维后的特征保存到这个文件夹下
    svs_dir = "D:/zm_scientific_research/svs_features"
    # 如果指定的文件夹不存在，就自动创建它；如果已经存在，就跳过，不报错。
    os.makedirs(svs_dir, exist_ok=True)
    paths = get_static_file_path("D:/zm_scientific_research/supervised_cutting")
    for _, path in enumerate(tqdm(paths, desc="svs文件特征提取")):
        j = os.path.join(path, "valid/csv/features.csv")
        logging.info("进行 %s 特征的重要选举与降维, 作为svs的特征表示。", j)
        # 可以传入 k * pca_dim，k表示提取多少个向量，pca_dim表示向量的维度
        patches = extract_topk_patches(j)
        csvPath = os.path.join(svs_dir, f"{get_last_file_name(path)}.csv")
        np.savetxt(csvPath, patches, delimiter=",")
